<?php
namespace TbfDatabase\SetTableData;

/**
 * 设置数据库某些张表的数据
 * 重点优化:时间性能
 * 目前检测下列输入错误
 * 1.表不存在
 * 2.字段不存在
 * 3.输入格式错误
 * */
class SetTableData
{
    /** @var  MergeSql */
    protected $mergeSql;
    protected $db_array = array();

    /**
     * 根据测试的mock数据重置数据库
     * @param $dbArray array map[string][]map[string]string
     * @return void
     * 输入格式:
     *   array(
     *    'cart_man'=>array(
     *       array(
     *         'manid'=>1,
     *         'ispublish'=>'1',
     *       )
     *     )
     *  )
     * 可以缺少某些字段:表示不关心那些字段的值,
     *
     * TODO: 使用原先Tbf的优化方案,但是不依赖dbal
     * TODO: 根据数据库已有信息将操作分为delete update insert 分别进行大批量操作
     * TODO: 使用show table status里面的rows的值,快速过滤表是否有变化(当变化时,不查询select *)(提升水平太小,仅占0.13%)
     */
    public function setTableData($dbArray)
    {
        $this->mergeSql = new MergeSql();
        foreach ($dbArray as $table_name => $expect_data) {
            $this->setOneTableData($table_name, $expect_data);
        }
        if ($this->mergeSql->isEmptyQuery()) {
            return;
        }
        $this->mergeSql->addQueryBefore(MergeSql::BEFORE_TRANSACTION,'SET FOREIGN_KEY_CHECKS=0;SET UNIQUE_CHECKS = 0');
        $this->mergeSql->addQuery(MergeSql::AFTER_TRANSACTION,'SET FOREIGN_KEY_CHECKS=1;SET UNIQUE_CHECKS = 1;');
        $this->mergeSql->runAllQuery();
    }

    /**
     * 设置一张表的数据
     * 目标: 将这张表上的数据设置为需要设置的数据,自增键的值必须是最大id+1 时间性能尽可能高
     * 环境: 很多时候,其实这张表上的数据就是需要数据,没有任何变化,或者有的几行变化
     * 实现:
     * 1.确定自增键的值是否比需要的大 show table status 获取主键的名称
     *    如果比需要的大
     *        1.->truncate table -> 重新插入所有数据
     *        2.-> 根据原有数据分别进行 delete update insert ->alter table
     * 2.确定现有数据是否和需要的数据完全一致 select * from
     *    如果一致,什么也不做
     * 3.根据原有数据分别进行 delete update insert
     * @param $table_name 表名
     * @param $expect_data 需要设置上去的数据
     */
    protected function setOneTableData($table_name, $expect_data)
    {
        $this->mergeSql->addQuery(MergeSql::BEFORE_TRANSACTION,'truncate `' . $table_name . '`');
        //批量插入
        $this->bulkInsert($table_name, $expect_data);
    }

    /**
     * 同一张表上 大批量快速插入
     * 1.输入数据必须是[]map[string]string 格式
     */
    protected function bulkInsert($table_name, $data)
    {
        $i = 0;
        $cols = array();
        foreach ($data as $row) {
            if (!is_array($row)){
                throw new \Exception('bulkInsert wrong data format!!type:[]map[string]string');
            }
            foreach ($row as $columnName => $value) {
                $cols[$i]['column'][$columnName] = 1;
                $cols[$i]['placeholder_array'][] = '?';
                $cols[$i]['value'][] = $value;
            }
            $cols[$i]['placeholder'] = '(' . implode(', ', $cols[$i]['placeholder_array']) . ')';
            $i++;
        }
        //没有数据就退出了
        if (empty($cols)) {
            return;
        }
        //每一行的列不一定是一样的...
        while (!empty($cols)) {
            $this_cols = array(array_shift($cols)); //尽量保持插入顺序
            foreach ($cols as $i => $col) {
                if ($col['column'] === $this_cols[0]['column']) {
                    $this_cols[] = $col;
                    unset($cols[$i]);
                }
            }
            $placeholders = array();
            $values = array();
            foreach ($this_cols as $i => $col) {
                $placeholders[] = $col['placeholder'];
                $values = array_merge($values, $col['value']);
            }
            $col_name_list = array_keys($this_cols[0]['column']);
            $sql = 'INSERT INTO ' . $table_name
                . ' (`' . implode('`, `', $col_name_list) . '`)'
                . ' VALUES ' . implode(', ', $placeholders);
            $this->mergeSql->addQuery(MergeSql::TRANSACTION,$sql,$values);
        }
    }
}
